﻿using System;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using System.Diagnostics;
using System.ComponentModel;

namespace a15_MTA_calcPrime_ByAsyncOperation
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool goOn = false;
        private long curPrime = 3;

        private Stopwatch stopWatch;

        private int uiThreadId;


        private delegate void CalcPrimeDelegate(AsyncOperation asyncOp);


        public MainWindow()
        {
            InitializeComponent();
            stopWatch = new Stopwatch();
        }

        private void StartOrStopCalc(object sender, RoutedEventArgs e)
        {
            //uiThreadId = Thread.CurrentThread.ManagedThreadId;

            if (goOn)
            {
                goOn = false;
                startOrStopBtn.Content = "Resume";
                Debug.WriteLine("Click to stop calc prime, threadId=" + Thread.CurrentThread.ManagedThreadId);
            }
            else
            {
                goOn = true;
                startOrStopBtn.Content = "Stop";

                Debug.WriteLine("Click to start calc prime, threadId=" + Thread.CurrentThread.ManagedThreadId);

                StartCalcAsync();
            }
        }

        private void StartCalcAsync()
        {
            //对于win form和wpf, 其synchronizationContext对应的分别是WindowsFormsSynchronizationContext和DispatcherSynchronizationContext,
            //AsyncOperationManager.CreateOperation()创建的asyncOp会记忆此时的thread, 在asyncOp.Post()调用时, 会发送到该thread执行.
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);

            CalcPrimeDelegate calcDelegate = new CalcPrimeDelegate(CheckNextPrime);

            //标准的async on delegate调用, 但delegate上入参是AsyncOperation
            //异步到worker thread上执行calcDelegate.
            calcDelegate.BeginInvoke(asyncOp, null, null);

        }

        //runing in worker thread
        private void CheckNextPrime(AsyncOperation asyncOp)
        {
            Debug.WriteLine("Calc next prime, curPrime=" + curPrime + ", threadId=" + Thread.CurrentThread.ManagedThreadId);

            stopWatch.Start();

            bool notPrime = false;

            for (long i = 3; i <= Math.Sqrt(curPrime); i++)
            {
                if (curPrime % i == 0)
                {
                    // Set not a prime flag to true.
                    notPrime = true;
                    break;
                }
            }

            CalculatePrimeCompletedEventArgs e = new CalculatePrimeCompletedEventArgs();

            // If a prime number.
            if (!notPrime)
            {
                stopWatch.Stop();
                e.prime = curPrime;
                e.elapsed = stopWatch.ElapsedMilliseconds;
            }

            curPrime += 2;

            //回调
            //asyncOp.Post()和PostOperationCompleted()会自动导向asyncOp创建时的thread, 在其上执行SendOrPostCallback
            asyncOp.PostOperationCompleted(new SendOrPostCallback(CalculateCompleted), e);
        }

        //running in ui thread
        private void CalculateCompleted(object e)
        {
            Debug.WriteLine("CalculateCompleted, tid=" + Thread.CurrentThread.ManagedThreadId);

            CalculatePrimeCompletedEventArgs ce =  e as CalculatePrimeCompletedEventArgs;

            UpdatePrimeUi(ce.prime, ce.elapsed);
        }

        // running in ui thread
        private void UpdatePrimeUi(long bigPrimeNum, long elapsedMSec)
        {
            Debug.WriteLine("Update UI, threadId=" + Thread.CurrentThread.ManagedThreadId);

            if (bigPrimeNum > 0)
            {
                bigPrime.Text = "Bigges prime: " + bigPrimeNum;
                elapsedTime.Text = "Elapsed time: " + elapsedMSec;
            }

            if (goOn)
            {
                Debug.WriteLine("Trig next calc");
                StartCalcAsync();
            }
        }

    }

    //hold the calc result
    public class CalculatePrimeCompletedEventArgs
    {
        public long prime = 0;
        public long elapsed = 0;
    }

}
